export default {
    name: 'keep-alive',
    abstract: true, // 此组件是否需要渲染成真实的dom
    props: {
        include: patternTypes,
        exclude: patternTypes,
        max: [String, Number]
    },
    created() {
        this.cache = Object.create(null) // 缓存dom
        this.keys = [] // 缓存key
    },
    mounted() {
        this.$watch('include', val => {
            pruneCache(this, name => matches(val, name))
        }),
            this.$watch('exclude', val => {
                pruneCache(this, name => !matches(val, name))
            })
    },
    destroyed() {
        for (const key in this.cache) { // 删除所有的缓存
            pruneCacheEntry(this.cache, key, this.keys)
        }
    },
    render() {
        const slot = this.$slot.default // 获取到keep-alive包裹的组件
        const vnode = getFirstComponentChild(slot) // 找到第一个子组件对象
        const componentOptions = vnode && vnode.componentOptions
        if (componentOptions) // 存在组件参数
        {
            const name = getComponentName(componentOptions) // 获取组件名称
            const { include, exclude } = this
            if ((include && (!name || !matches(include, name)))
                || (exclude && name && matches(exclude, name))) {
                return vnode
            }

            const { cache, keys } = this
            const key = vnode.key == null // 定义组件的缓存key
                ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
                : vnode.key

            if (cache[key]) { // 组件是否被缓存过了
                vnode.componentInstance = cache[key].componentInstance
                remove(keys, key)
                keys.push(key) // 更新缓存key的位置, 调整key的排序
            } else {
                cache[key] = vnode // 缓存组件对象
                keys.push(key)
                if (this.amx && keys.length > parseInt(this.max)) { // 超出缓存的数量限制，移除第一个缓存
                    pruneCacheEntry(cache, key[0], keys, this_vnode)
                }
            }

            vnode.data.keepAlive = true // 该属性在渲染和执行被包裹的钩子函数时需要用到
        }
        return vnode || (slot && slot(0))
    },
}


function pruneCacheEntry(
    cache = {},
    key = '',
    keys = [],
    _cuttent = {}
) {
    const cached = cache[key]
    if (cache && (!current || cached.tag !== current.tag)) {
        cached.componentInstance.$destroy()  // 执行自测的destroy生命周期
    }
    cache[key] = null
    remove(keys, key) // 移除
}